using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Volo.Abp;
using Volo.Abp.BackgroundJobs;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Timing;
using Matrix.Notification.Application.Logging;
using Matrix.Notification.Domain.Notifications;
using Matrix.Notification.Services;

namespace Matrix.Notification.Channels
{
    /// <summary>
    /// WebHook通知渠道实现
    /// </summary>
    public class WebHookNotificationChannel : INotificationChannel, ITransientDependency
    {
        public string ChannelType => "WebHook";

        private readonly ILogger<WebHookNotificationChannel> _logger;
        private readonly IWebHookNotificationService _webHookService;
        private readonly IClock _clock;
        private readonly IBackgroundJobManager _backgroundJobManager;
        private readonly WebHookNotificationChannelOptions _options;

        public WebHookNotificationChannel(
            ILogger<WebHookNotificationChannel> logger,
            IWebHookNotificationService webHookService,
            IClock clock,
            IBackgroundJobManager backgroundJobManager,
            IOptions<WebHookNotificationChannelOptions> options)
        {
            _logger = logger;
            _webHookService = webHookService;
            _clock = clock;
            _backgroundJobManager = backgroundJobManager;
            _options = options.Value;
        }

        public Task<bool> IsAvailableAsync(CancellationToken cancellationToken = default)
        {
            return Task.FromResult(_options.IsEnabled && _webHookService != null);
        }

        public async Task<NotificationChannelResult> SendAsync(
            object notification,
            CancellationToken cancellationToken = default)
        {
            var result = new NotificationChannelResult();

            try
            {
                var webhookNotification = notification as WebHookNotificationMessage;
                if (webhookNotification == null)
                {
                    _logger.LogError("通知对象不是WebHook类型");
                    return CreateFailureResult("通知对象不是WebHook类型");
                }

                using (var scope = _logger.LogNotificationSending(
                    webhookNotification.Id,
                    NotificationChannel.WebHook,
                    webhookNotification.WebHookUrl))
                {
                    var sendResult = await SendWebHookNotificationAsync(webhookNotification, cancellationToken);

                    if (sendResult.Success)
                    {
                        result.IsSuccess = true;
                        result.SuccessCount = 1;
                        result.MessageId = sendResult.RequestId;
                        result.SentAt = _clock.Now;

                        _logger.LogNotificationSent(
                            webhookNotification.Id,
                            NotificationChannel.WebHook,
                            sendResult.RequestId,
                            _clock.Now - webhookNotification.CreationTime);
                    }
                    else
                    {
                        result.IsSuccess = false;
                        result.FailureCount = 1;
                        result.ErrorMessage = sendResult.ErrorMessage;

                        // 如果启用重试且错误类型支持重试，则安排重试
                        if (_options.EnableRetry && ShouldRetry(sendResult))
                        {
                            await ScheduleRetryAsync(webhookNotification, sendResult, cancellationToken);
                        }

                        _logger.LogNotificationSendFailed(
                            webhookNotification.Id,
                            NotificationChannel.WebHook,
                            sendResult.ErrorMessage ?? "Unknown error",
                            sendResult.RetryCount,
                            _clock.Now - webhookNotification.CreationTime);
                    }
                }

                return result;
            }
            catch (Exception ex)
            {
                _logger.LogNotificationError(
                    ex,
                    "SendWebHook",
                    notification is WebHookNotificationMessage ? ((WebHookNotificationMessage)notification).Id : null);

                return CreateFailureResult(ex.Message);
            }
        }

        public async Task<NotificationChannelResult> SendBatchAsync(
            IReadOnlyCollection<object> notifications,
            CancellationToken cancellationToken = default)
        {
            var result = new NotificationChannelResult();
            var startTime = _clock.Now;
            var webhookNotifications = notifications.Cast<WebHookNotificationMessage>().ToList();

            try
            {
                _logger.LogInformation("开始批量发送WebHook通知: {Count}个通知", webhookNotifications.Count);

                // 按URL分组以优化批量发送
                var groupedNotifications = webhookNotifications
                    .Where(n => !string.IsNullOrEmpty(n.WebHookUrl))
                    .GroupBy(n => n.WebHookUrl!)
                    .ToList();

                foreach (var group in groupedNotifications)
                {
                    var url = group.Key;
                    var notificationsForUrl = group.ToList();

                    try
                    {
                        // 尝试批量发送到同一URL
                        if (notificationsForUrl.Count > 1)
                        {
                            var batchResults = await SendBatchToUrlAsync(notificationsForUrl, url, cancellationToken);

                            foreach (var batchResult in batchResults)
                            {
                                if (batchResult.Success)
                                {
                                    result.SuccessCount++;
                                }
                                else
                                {
                                    result.FailureCount++;
                                    result.ErrorMessage = batchResult.ErrorMessage;
                                }
                            }
                        }
                        else
                        {
                            // 单个通知发送
                            var singleResult = await SendAsync(notificationsForUrl.First(), cancellationToken);

                            if (singleResult.IsSuccess)
                            {
                                result.SuccessCount++;
                            }
                            else
                            {
                                result.FailureCount++;
                                result.ErrorMessage = singleResult.ErrorMessage;
                            }
                        }
                    }
                    catch (Exception ex)
                    {
                        _logger.LogError(ex, "向URL发送WebHook通知失败: {Url}, Count={Count}",
                            MaskUrl(url), notificationsForUrl.Count);

                        result.FailureCount += notificationsForUrl.Count;
                        result.ErrorMessage = ex.Message;
                    }
                }

                result.IsSuccess = result.FailureCount == 0;
                result.SentAt = _clock.Now;

                var duration = _clock.Now - startTime;
                _logger.LogInformation(
                    "批量WebHook通知发送完成: 总数={Total}, 成功={Success}, 失败={Failed}, 耗时={Duration}ms",
                    webhookNotifications.Count,
                    result.SuccessCount,
                    result.FailureCount,
                    duration.TotalMilliseconds);

                return result;
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "批量WebHook通知发送失败: {Error}", ex.Message);
                return CreateFailureResult(ex.Message);
            }
        }

        #region 私有方法

        private async Task<WebHookNotificationResult> SendWebHookNotificationAsync(
            WebHookNotificationMessage webhookNotification,
            CancellationToken cancellationToken = default)
        {
            // 验证URL
            if (!await _webHookService.ValidateWebHookUrlAsync(webhookNotification.WebHookUrl, cancellationToken))
            {
                return new WebHookNotificationResult
                {
                    Success = false,
                    WebHookUrl = webhookNotification.WebHookUrl,
                    ErrorMessage = "无效的WebHook URL",
                    ErrorType = WebHookErrorType.FormatError,
                    RequestTime = _clock.Now,
                    ResponseTime = _clock.Now
                };
            }

            // 应用转换器
            var transformedNotification = await ApplyTransformersAsync(webhookNotification, cancellationToken);

            // 发送通知
            return await _webHookService.SendAsync(transformedNotification, webhookNotification.WebHookUrl, cancellationToken);
        }

        private async Task<List<WebHookNotificationResult>> SendBatchToUrlAsync(
            List<WebHookNotificationMessage> notifications,
            string url,
            CancellationToken cancellationToken = default)
        {
            var results = new List<WebHookNotificationResult>();

            // 创建聚合通知
            var batchNotification = CreateBatchNotification(notifications);

            try
            {
                var batchResult = await _webHookService.SendAsync(batchNotification, url, cancellationToken);

                // 为每个原始通知创建结果
                foreach (var notification in notifications)
                {
                    results.Add(new WebHookNotificationResult
                    {
                        Success = batchResult.Success,
                        RequestId = batchResult.RequestId,
                        WebHookUrl = url,
                        StatusCode = batchResult.StatusCode,
                        ResponseContent = batchResult.ResponseContent,
                        ErrorMessage = batchResult.ErrorMessage,
                        ErrorType = batchResult.ErrorType,
                        RequestTime = batchResult.RequestTime,
                        ResponseTime = batchResult.ResponseTime,
                        RetryCount = batchResult.RetryCount,
                        Metadata = new Dictionary<string, object>(batchResult.Metadata)
                        {
                            ["OriginalNotificationId"] = notification.Id,
                            ["IsBatch"] = true,
                            ["BatchSize"] = notifications.Count
                        }
                    });
                }

                return results;
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "批量WebHook发送失败: Url={Url}, Count={Count}", MaskUrl(url), notifications.Count);

                // 为每个通知创建失败结果
                foreach (var notification in notifications)
                {
                    results.Add(new WebHookNotificationResult
                    {
                        Success = false,
                        WebHookUrl = url,
                        ErrorMessage = ex.Message,
                        ErrorType = WebHookErrorType.UnknownError,
                        RequestTime = _clock.Now,
                        ResponseTime = _clock.Now,
                        Metadata = new Dictionary<string, object>
                        {
                            ["OriginalNotificationId"] = notification.Id,
                            ["IsBatch"] = true,
                            ["BatchSize"] = notifications.Count
                        }
                    });
                }

                return results;
            }
        }

        private WebHookNotificationMessage CreateBatchNotification(List<WebHookNotificationMessage> notifications)
        {
            var firstNotification = notifications.First();

            return new WebHookNotificationMessage
            {
                Id = Guid.NewGuid().ToString(),
                Title = $"批量通知 ({notifications.Count}条)",
                Content = $"包含{notifications.Count}条通知的批量消息",
                ContentType = firstNotification.ContentType ?? "application/json",
                Method = firstNotification.Method,
                WebHookUrl = firstNotification.WebHookUrl,
                Headers = new Dictionary<string, string>(firstNotification.Headers ?? new())
                {
                    ["X-Batch-Size"] = notifications.Count.ToString(),
                    ["X-Batch-Id"] = Guid.NewGuid().ToString()
                },
                Data = new Dictionary<string, object>
                {
                    ["batch"] = true,
                    ["count"] = notifications.Count,
                    ["notifications"] = notifications.Select(n => new
                    {
                        id = n.Id,
                        title = n.Title,
                        content = n.Content,
                        timestamp = n.Timestamp,
                        data = n.Data
                    }).ToList()
                },
                Timestamp = _clock.Now,
                RetryPolicy = firstNotification.RetryPolicy,
                TimeoutSeconds = firstNotification.TimeoutSeconds
            };
        }

        private async Task<WebHookNotificationMessage> ApplyTransformersAsync(
            WebHookNotificationMessage notification,
            CancellationToken cancellationToken = default)
        {
            var transformedNotification = notification;

            // 应用通用转换器
            foreach (var transformer in _options.Transformers)
            {
                try
                {
                    if (transformer.Enabled)
                    {
                        transformedNotification = await transformer.TransformAsync(transformedNotification, cancellationToken);
                    }
                }
                catch (Exception ex)
                {
                    _logger.LogWarning(ex, "应用WebHook转换器失败: Transformer={Transformer}, NotificationId={NotificationId}",
                        transformer.Name, notification.Id);
                }
            }

            // 添加通用头信息
            if (transformedNotification.Headers == null)
            {
                transformedNotification.Headers = new Dictionary<string, string>();
            }

            transformedNotification.Headers.TryAdd("X-Matrix-Notification-ID", notification.Id);
            transformedNotification.Headers.TryAdd("X-Matrix-Timestamp", DateTimeOffset.UtcNow.ToUnixTimeSeconds().ToString());
            transformedNotification.Headers.TryAdd("X-Matrix-Source", "Matrix.Notification");

            return transformedNotification;
        }

        private bool ShouldRetry(WebHookNotificationResult result)
        {
            if (!_options.EnableRetry)
            {
                return false;
            }

            // 检查重试次数
            if (result.RetryCount >= _options.MaxRetryCount)
            {
                return false;
            }

            // 检查错误类型是否支持重试
            if (result.ErrorType.HasValue)
            {
                return _options.RetryableErrorTypes.Contains(result.ErrorType.Value);
            }

            // 检查HTTP状态码是否支持重试
            return _options.RetryableStatusCodes.Contains(result.StatusCode);
        }

        private async Task ScheduleRetryAsync(
            WebHookNotificationMessage notification,
            WebHookNotificationResult failedResult,
            CancellationToken cancellationToken = default)
        {
            try
            {
                var delay = CalculateRetryDelay(failedResult.RetryCount);

                _logger.LogInformation(
                    "安排WebHook重试: NotificationId={NotificationId}, RetryCount={RetryCount}, Delay={Delay}秒",
                    notification.Id, failedResult.RetryCount, delay.TotalSeconds);

                await _backgroundJobManager.EnqueueAsync(
                    new WebHookRetryJobArgs
                    {
                        Notification = notification,
                        FailedResult = failedResult,
                        RetryAttempt = failedResult.RetryCount + 1,
                        ScheduledTime = _clock.Now.Add(delay)
                    },
                    BackgroundJobPriority.Low,
                    cancellationToken: cancellationToken);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "安排WebHook重试失败: NotificationId={NotificationId}", notification.Id);
            }
        }

        private TimeSpan CalculateRetryDelay(int retryCount)
        {
            // 指数退避算法
            var baseDelay = TimeSpan.FromSeconds(_options.BaseRetryDelaySeconds);
            var maxDelay = TimeSpan.FromSeconds(_options.MaxRetryDelaySeconds);

            var delay = TimeSpan.FromSeconds(
                Math.Min(
                    baseDelay.TotalSeconds * Math.Pow(2, retryCount),
                    maxDelay.TotalSeconds));

            // 添加随机抖动，避免雷群效应
            var jitter = new Random().NextDouble() * 0.1; // 0-10%的抖动
            delay = TimeSpan.FromSeconds(delay.TotalSeconds * (1 + jitter));

            return delay;
        }

        private NotificationChannelResult CreateFailureResult(string errorMessage)
        {
            return new NotificationChannelResult
            {
                IsSuccess = false,
                ErrorMessage = errorMessage,
                SuccessCount = 0,
                FailureCount = 1,
                SentAt = _clock.Now
            };
        }

        private string MaskUrl(string url)
        {
            if (string.IsNullOrEmpty(url))
            {
                return "***";
            }

            try
            {
                var uri = new Uri(url);
                return $"{uri.Scheme}://{uri.Host}/***";
            }
            catch
            {
                return url.Length > 10 ? url.Substring(0, 10) + "***" : "***";
            }
        }

        #endregion
    }

    #region 后台任务

    /// <summary>
    /// WebHook重试任务参数
    /// </summary>
    public class WebHookRetryJobArgs
    {
        public WebHookNotificationMessage Notification { get; set; } = null!;
        public WebHookNotificationResult FailedResult { get; set; } = null!;
        public int RetryAttempt { get; set; }
        public DateTime ScheduledTime { get; set; }
    }

    /// <summary>
    /// WebHook重试后台任务
    /// </summary>
    public class WebHookRetryJob : AsyncBackgroundJob<WebHookRetryJobArgs>
    {
        private readonly IWebHookNotificationService _webHookService;
        private readonly ILogger<WebHookRetryJob> _logger;

        public WebHookRetryJob(
            IWebHookNotificationService webHookService,
            ILogger<WebHookRetryJob> logger)
        {
            _webHookService = webHookService;
            _logger = logger;
        }

        public override async Task ExecuteAsync(WebHookRetryJobArgs args)
        {
            try
            {
                _logger.LogInformation(
                    "执行WebHook重试任务: NotificationId={NotificationId}, Attempt={Attempt}",
                    args.Notification.Id, args.RetryAttempt);

                var retryResult = await _webHookService.RetryAsync(
                    args.FailedResult,
                    maxRetries: 1);

                if (retryResult.Success)
                {
                    _logger.LogInformation(
                        "WebHook重试成功: NotificationId={NotificationId}, TotalRetries={TotalRetries}",
                        args.Notification.Id, args.RetryAttempt);
                }
                else
                {
                    _logger.LogWarning(
                        "WebHook重试失败: NotificationId={NotificationId}, Attempt={Attempt}, Error={Error}",
                        args.Notification.Id, args.RetryAttempt, retryResult.ErrorMessage);
                }
            }
            catch (Exception ex)
            {
                _logger.LogError(ex,
                    "WebHook重试任务执行异常: NotificationId={NotificationId}, Attempt={Attempt}",
                    args.Notification.Id, args.RetryAttempt);
            }
        }
    }

    #endregion

    #region 配置选项

    /// <summary>
    /// WebHook通知渠道配置选项
    /// </summary>
    public class WebHookNotificationChannelOptions
    {
        /// <summary>
        /// 是否启用
        /// </summary>
        public bool IsEnabled { get; set; } = true;

        /// <summary>
        /// 是否启用重试
        /// </summary>
        public bool EnableRetry { get; set; } = true;

        /// <summary>
        /// 最大重试次数
        /// </summary>
        public int MaxRetryCount { get; set; } = 3;

        /// <summary>
        /// 基础重试延迟（秒）
        /// </summary>
        public int BaseRetryDelaySeconds { get; set; } = 30;

        /// <summary>
        /// 最大重试延迟（秒）
        /// </summary>
        public int MaxRetryDelaySeconds { get; set; } = 3600;

        /// <summary>
        /// 可重试的错误类型
        /// </summary>
        public List<WebHookErrorType> RetryableErrorTypes { get; set; } = new()
        {
            WebHookErrorType.NetworkError,
            WebHookErrorType.TimeoutError,
            WebHookErrorType.ServiceUnavailable,
            WebHookErrorType.HttpError
        };

        /// <summary>
        /// 可重试的HTTP状态码
        /// </summary>
        public List<int> RetryableStatusCodes { get; set; } = new()
        {
            408, // Request Timeout
            429, // Too Many Requests
            500, // Internal Server Error
            502, // Bad Gateway
            503, // Service Unavailable
            504  // Gateway Timeout
        };

        /// <summary>
        /// 默认Content-Type
        /// </summary>
        public string DefaultContentType { get; set; } = "application/json";

        /// <summary>
        /// 默认HTTP方法
        /// </summary>
        public string DefaultHttpMethod { get; set; } = "POST";

        /// <summary>
        /// 请求转换器列表
        /// </summary>
        public List<IWebHookTransformer> Transformers { get; set; } = new();

        /// <summary>
        /// 单次批量发送最大数量
        /// </summary>
        public int MaxBatchSize { get; set; } = 100;

        /// <summary>
        /// 批量发送间隔（毫秒）
        /// </summary>
        public int BatchSendInterval { get; set; } = 1000;
    }

    /// <summary>
    /// WebHook转换器接口
    /// </summary>
    public interface IWebHookTransformer
    {
        /// <summary>
        /// 转换器名称
        /// </summary>
        string Name { get; }

        /// <summary>
        /// 是否启用
        /// </summary>
        bool Enabled { get; set; }

        /// <summary>
        /// 转换WebHook通知消息
        /// </summary>
        /// <param name="notification">原始通知消息</param>
        /// <param name="cancellationToken">取消令牌</param>
        /// <returns>转换后的通知消息</returns>
        Task<WebHookNotificationMessage> TransformAsync(
            WebHookNotificationMessage notification,
            CancellationToken cancellationToken = default);
    }

    #endregion
}